package edu.caltech.nanodb.plans;


import java.io.IOException;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import edu.caltech.nanodb.commands.SelectValue;


import edu.caltech.nanodb.expressions.Expression;
import edu.caltech.nanodb.expressions.OrderByExpression;
import edu.caltech.nanodb.expressions.TupleLiteral;

import edu.caltech.nanodb.functions.AggregateFunction;

import edu.caltech.nanodb.qeval.PlanCost;
import edu.caltech.nanodb.relations.ColumnInfo;
import edu.caltech.nanodb.relations.Tuple;


/**
 */
public abstract class HashedGroupAggregateNode extends GroupAggregateNode {

    /**
     * This hash-map holds the various groups that have been encountered so far
     * in the input data, along with the aggregate computations that must be
     * performed.
     */
    private LinkedHashMap<TupleLiteral, ArrayList<AggregateFunction>> aggregates;


    public HashedGroupAggregateNode(List<Expression> groupByExprs,
                                    List<SelectValue> selectValues) {
        super(groupByExprs, selectValues);
    }


    @Override
    public boolean equals(Object obj) {
        if (obj instanceof HashedGroupAggregateNode) {
            HashedGroupAggregateNode other = (HashedGroupAggregateNode) obj;

            return groupByExprs.equals(other.groupByExprs) &&
                   selectValues.equals(other.selectValues) &&
                   leftChild.equals(other.leftChild);
        }
        return false;
    }


    @Override
    public int hashCode() {
        int hash = 17;
        hash = 31 * hash + groupByExprs.hashCode();
        hash = 31 * hash + selectValues.hashCode();
        hash = 31 * hash + leftChild.hashCode();
        return hash;
    }


    // Use parent javadocs.
    @Override
    public String toString() {
        return "HashedGroupAggregate[groupBy=" + groupByExprs + ", values=" +
            selectValues + "]";
    }


    /**
     * The hashed grouping/aggregate operation does not order its results in
     * any way.
     */
    public List<OrderByExpression> resultsOrderedBy() {
        return null;
    }


    /**
     * The hashed grouping/aggregate operation does not support marking, since
     * it must fully consume its input before it can produce any results.
     */
    public boolean supportsMarking() {
        return false;
    }


    /**
     * The hashed grouping/aggregate operation does not require marking.
     */
    public boolean requiresLeftMarking() {
        return false;
    }


    /**
     * The hashed grouping/aggregate operation does not require marking.
     */
    public boolean requiresRightMarking() {
        return false;
    }


    /**
     * Gets the next tuple that fulfills the conditions for this plan node.
     * If the node has a child, it should call getNextTuple() on the child.
     * If the node is a leaf, the tuple comes from some external source such
     * as a table file, the network, etc.
     *
     * @return the next tuple to be generated by this plan, or <tt>null</tt>
     *         if the plan has finished generating plan nodes.
     *
     * @throws java.io.IOException if table data cannot be read from the filesystem
     * @throws IllegalStateException if a plan node is not properly initialized
     */
    public Tuple getNextTuple()
        throws IllegalStateException, IOException {

        return null;
    }


    /**
     * Computes the cost of this plan node's operation.  The computation will
     * depend on which algorithm the node uses and the data it is working with.
     *
     * @return an object containing various cost measures such as the worst-case
     *         number of disk accesses, the number of tuples produced, etc.
     */
    public PlanCost estimateCost() {
        // TODO
        return null;
    }


    /**
     * Does any initialization the node might need.  This could include
     * resetting state variables or starting the node over from the beginning.
     *
     */
    public void initialize() {
        super.initialize();
        leftChild.initialize();
    }


    /**
     * The grouping/aggregate node produces a very different schema from its
     * input relation.
     *
     * @return The new schema produced by this node
     */
    public List<ColumnInfo> getColumnInfos() {
        // TODO
        return null;
    }


    /**
     * The hashed grouping/aggregate plan node doesn't support marking.
     *
     * @throws UnsupportedOperationException always.
     */
    public void markCurrentPosition() throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
            "Hashed grouping/aggregate node doesn't support marking");
    }


    /**
     * The hashed grouping/aggregate plan node doesn't support marking.
     *
     * @throws UnsupportedOperationException always.
     */
    public void resetToLastMark() {
        throw new UnsupportedOperationException(
            "Hashed grouping/aggregate node doesn't support marking");
    }


    /**
     * Perform any necessary clean up tasks. This should probably be called
     * when we are done with this plan node.
     */
    public void cleanUp() {
        leftChild.cleanUp();
    }
}
